package cn.flying.cloud.tool.ops.instance.websocket;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;

import cn.flying.cloud.base.common.exception.BizException;
import cn.flying.cloud.base.common.utils.Ft;
import cn.flying.cloud.tool.ops.instance.entity.InstanceInfo;
import cn.flying.cloud.tool.ops.instance.service.InstanceInfoService;

/**
 * 查看实例日志websocket端点服务
 *
 * @author: admin
 * @date: 2025年04月26日 19:15
 * @version: 1.0
 */
@Component
@ServerEndpoint("/websocket/admin/instance/log/{clientId}/{id}")
public class TopicInstancePush {
    private static final Logger logger = LoggerFactory.getLogger(TopicInstancePush.class);

    /**
     * 存储客户端session信息
     */
    public static Map<String, Session> clients = new ConcurrentHashMap<>();
    /**
     * 存储ssh连接session信息
     */
    public static Map<String, com.jcraft.jsch.Session> jschSessions = new ConcurrentHashMap<>();
    public static Map<String, ChannelExec> channels = new ConcurrentHashMap<>();

    /**
     * 当与前端的websocket连接成功时，执行该方法
     * 获取ServerEndpoint路径中的占位符信息类似 控制层的 @PathVariable注解
     *
     * @param session  客户端连接session
     * @param clientId 传入的客户端ID
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("clientId") String clientId, @PathParam("id") String id) {
        // 缓存客户端及其session
        clients.put(clientId, session);
        logger.info("客户端【{}】建立连接，会话ID【{}】连接开启！", clientId, session.getId());

        InstanceInfoService infoService = Ft.Spring.getBean(InstanceInfoService.class);
        InstanceInfo info = infoService.getObjById(id);
        if (info == null) {
            throw new BizException("访问的服务器不存在！");
        }
        try {
            JSch jsch = new JSch();
            com.jcraft.jsch.Session jschSession = jsch.getSession(info.getUsername(), info.getHost(), 22);
            jschSession.setPassword(info.getPassword());
            jschSession.setConfig("StrictHostKeyChecking", "no");
            jschSession.connect();

            jschSessions.put(clientId, jschSession);

            ChannelExec channel = (ChannelExec) jschSession.openChannel("exec");
            channels.put(clientId, channel);

            //示例："cd /home/app/admin-server/logs && tail -f flying-cloud-admin-0.6.0-SNAPSHOT.log";
            String command = "cd " + info.getPath() + "/logs && tail -f " + info.getArtifactId() + "-" + info.getVersion() + ".log";
            logger.info("执行命令；【{}】", command);
            channel.setCommand(command);
            channel.setInputStream(null);
            channel.setErrStream(System.err);
            channel.connect();

            InputStream in = channel.getInputStream();

            // 指定编码格式读取输入流
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
            String line;
            while ((line = reader.readLine()) != null) {
                // 推送到前端页面
                sendMessage(clientId, line);
            }
        } catch (Exception e) {
            logger.error("访问远程服务器异常--->:", e);
            throw new BizException("访问远程服务器异常！");
        }
    }

    /**
     * 当连接关闭时，执行该方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("clientId") String clientId) {
        clients.remove(clientId);
        com.jcraft.jsch.Session jschSession = jschSessions.get(clientId);
        ChannelExec channel = channels.get(clientId);
        channel.disconnect();
        jschSession.disconnect();
        logger.info("客户端【{}】断开了会话【{}】的连接！", clientId, session.getId());
    }

    /**
     * 当收到前台发送的消息时，执行该方法
     *
     * @param message 消息
     * @param session 连接session
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("clientId") String clientId) {
        logger.info("收到来自客户端：【{}】的消息！内容：【{}】", clientId, message);
    }

    /**
     * 当连接发生错误时，执行该方法
     *
     * @param error 异常
     */
    @OnError
    public void onError(Throwable error) {
        logger.info("系统错误--->", error);
    }


    /**
     * 发送信息到客户端
     *
     * @param clientId 客户端
     * @param message  消息
     */
    public static void sendMessage(String clientId, String message) {
        if (Ft.String.isBlank(clientId)) {
            return;
        }
        Session session = clients.get(clientId);
        //向会话发送消息
        if (Objects.isNull(session)) {
            return;
        }
        try {
            //同步发送数据，需要等上一个sendText发送完成才执行下一个发送
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            logger.error("向客户端【{}】推送消息失败！", clientId, e);
        }
    }
}